﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web.Mvc;

namespace Mvc.Razor
{
	/// <summary>
	/// Provides a default abstract implementation of the IFontAwesomeObject interface.
	/// </summary>
	[DefaultProperty("Html")]
	public abstract class MvcRazorObject : IMvcRazorBaseObject
	{
		private IDictionary<string, string> _classAttributes = new Dictionary<string, string>();
		private string _htmlTag = null;

		/// <summary>
		/// Gets/sets the HTML tag that is used to represent the object when the
		/// the HTML markup is generated. This value should be the text portion of
		/// the tag only.
		/// </summary>
		public virtual string HtmlTag
		{
			get
			{
				if (_htmlTag == null)
				{
					_htmlTag = this.OnInitializeHtmlTag();
				}

				return _htmlTag;
			}
			set
			{
				_htmlTag = value;
			}
		}

		/// <summary>
		/// Gets the set of class attributes used when formatting the TML tag.
		/// </summary>
		public virtual IDictionary<string, string> ClassAttributes
		{
			get
			{
				return _classAttributes;
			}
			protected set
			{
				_classAttributes = value;
			}
		}

		/// <summary>
		/// Add a custom class attribute to the set contained within this instance. The
		/// key and the value will contain the value of 'item'. Item is the value that 
		/// will be used when formatting the HTML class attribute.
		/// </summary>
		/// <param name="item"></param>
		public virtual void AddClassAttribute(string item)
		{
			this.ClassAttributes.Add(item, item);
		}

		/// <summary>
		/// Add a custom class attribute to the set contained within this instance. the key is used for 
		/// here for lookup and replacement of existing attribute. Item is the value that will be used when 
		/// formatting the HTML class attribute.
		/// </summary>
		/// <param name="key">A System.String value to provide a key to lookup the attribute in this set.</param>
		/// <param name="item">A System.String value containing the attribute to be included in the HTML markup.</param>
		public virtual void AddClassAttribute(string key, string item)
		{
			this.ClassAttributes.Add(key, item);
		}

		/// <summary>
		/// Update an exiting custom class attribute contained in this instance. The value for
		/// item is to lookup the existing attribute. If the value exists it will be replaced 
		/// otherwise it will added to the list.
		/// </summary>
		/// <param name="item">A System.String value containing the attribute to be included in the HTML markup.</param>
		public void UpdateClassAttribute(string item)
		{
			if (this.ClassAttributes.ContainsKey(item))
			{
				this.ClassAttributes[item] = item;
			}
			else
			{
				this.AddClassAttribute(item);
			}
		}

		/// <summary>
		/// Update an exiting custom class attribute contained in this instance. The value for
		/// key is used to lookup the existing attribute. If the value exists it will be 
		/// replaced otherwise it will added to the list.
		/// </summary>
		/// <param name="key">A System.String value to provide a key to lookup the attribute in this set.</param>
		/// <param name="item">A System.String value containing the attribute to be included in the HTML markup.</param>
		public void UpdateClassAttribute(string key, string item)
		{
			if (this.ClassAttributes.ContainsKey(key))
			{
				this.ClassAttributes[key] = item;
			}
			else
			{
				this.AddClassAttribute(key, item);
			}
		}

		/// <summary>
		/// Merges a set of class attributes with the current items.
		/// </summary>
		/// <param name="items">A list of attribute keys and values to be merged
		/// into the current set in this instance.</param>
		public void MergeClassAttributes(IDictionary<string, string> items)
		{
			if (items != null)
			{
				// ***
				// *** Make a copy of each class descriptor
				// ***
				foreach (var item in items)
				{
					this.UpdateClassAttribute(item.Key, item.Value);
				}
			}
		}

		/// <summary>
		/// Merges a set of class attributes with the current items.
		/// </summary>
		/// <param name="items">A list of attribute values to be merged
		/// into the current set in this instance.</param>
		public void MergeClassAttributes(IEnumerable<string> items)
		{
			if (items != null)
			{
				// ***
				// *** Make a copy of each class descriptor
				// ***
				foreach (var item in items)
				{
					this.UpdateClassAttribute(item);
				}
			}
		}

		/// <summary>
		/// Converts this instance of the Mvc.Razor.FontAwesome.FontAwesomeBase
		/// to an System.Web.Mvc.MvcHtmlString object containing the HTML markup 
		/// for the icon.
		/// </summary>
		/// <param name="item">The Mvc.Razor.FontAwesome.FontAwesomeBase instance to be converted.</param>
		/// <returns>A System.Web.Mvc.MvcHtmlString object containing the HTML markup for this Font Awesome icon.</returns>
		public static implicit operator MvcHtmlString(MvcRazorObject item)
		{
			return item.Html;
		}

		/// <summary>
		/// Converts this instance of the Mvc.Razor.FontAwesome.FontAwesomeBase
		/// to an System.String object containing the HTML markup for the icon.
		/// </summary>
		/// <param name="item">The Mvc.Razor.FontAwesome.FontAwesomeBase instance to be converted.</param>
		/// <returns>A System.String object containing the HTML markup for this Font Awesome icon.</returns>
		public static implicit operator string(MvcRazorObject item)
		{
			return item.Html.ToString();
		}

		/// <summary>
		/// Gets an MvcHtmlString object containing the HTML markup for this instance.
		/// </summary>
		public virtual MvcHtmlString Html
		{
			get
			{
				return this.OnGetHtml();
			}
		}

		/// <summary>
		/// Gets an MvcHtmlString object containing the HTML markup for this instance. This method must be overridden.
		/// </summary>
		/// <returns>A System.Web.Mvc.MvcHtmlString object containing the HTML markup for this Font Awesome icon.</returns>
		protected virtual MvcHtmlString OnGetHtml()
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Returns the HTML markup for the class attributes in this instance.
		/// </summary>
		protected MvcHtmlString ClassAttributeHtml
		{
			get
			{
				StringBuilder returnValue = new StringBuilder();

				if (this.ClassAttributes != null)
				{
					// ***
					// *** fa should be first
					// ***
					if (this.ClassAttributes.ContainsKey("fa"))
					{
						returnValue.Append(this.ClassAttributes["fa"]);
					}

					// ***
					// *** The icon Id is second
					// ***
					if (this.ClassAttributes.ContainsKey("Id"))
					{
						if (returnValue.Length > 0)
						{
							returnValue.Append(" ");
						}
						else if (returnValue.Length == 0)
						{
							returnValue.Append("fa ");
						}

						returnValue.Append(this.ClassAttributes["Id"]);
					}

					// ***
					// *** Write the remaining items
					// ***
					foreach (var item in this.ClassAttributes)
					{
						if (item.Key != "fa" && item.Key != "Id")
						{
							if (returnValue.Length > 0)
							{
								returnValue.Append(" ");
							}

							returnValue.Append(item.Value);
						}
					}
				}

				return new MvcHtmlString(returnValue.ToString());
			}
		}

		/// <summary>
		/// Returns a string that contains the HTML of the current object.
		/// </summary>
		/// <returns>A string that contains the HTML of the current object.</returns>
		public override string ToString()
		{
			return (string)this;
		}

		/// <summary>
		/// Returns a deep copy of this instance.
		/// </summary>
		/// <returns>A newly initialized MVC Razor object.</returns>
		public virtual object Clone()
		{
			return this.OnClone();
		}

		/// <summary>
		/// Allows the derived class to initialize the HTML Tag without
		/// the need to override the HtmlTag property.
		/// </summary>
		/// <returns></returns>
		protected virtual string OnInitializeHtmlTag()
		{
			return null;
		}

		/// <summary>
		/// Allows the derived class to respond to the Clone() method
		/// without the need to override the HtmlTag property.
		/// </summary>
		/// <returns>Returns a deep copy of this instance.</returns>
		protected virtual object OnClone()
		{
			return null;
		}		
	}
}